#![allow(non_snake_case)]
#![allow(unused_mut)]
#![allow(unreachable_code)]

#[macro_use]
extern crate log;

use std::sync::Arc;

/*

本库的功能是完美实现dll替换, 自动且无缝切换新旧动态库, 热更新功能, 它的前身借鉴于 "hot-lib-reloader", 但在它的
基础上却掉了影响性能的各种锁, 和状态, 增加了 "加载前", "卸载前" 等事件;

特性如下
1. 开箱即用, 如 "dlopen2" 无缝衔接, 直接调用dll中的方法, 无需其它转换
2. 零成本抽象, 不使用任何读写锁, 也没有用原子锁, 追求极致的性能
3. 可与业务代码深度融合, 有加载前, 卸载前等事件可以跟踪处理业务代码

*/
use dlopen2::wrapper::WrapperApi;
use hotload::types::Hotload;

// Generate the following rust calling methods, and generate rust types (Struct, Trait, Enum, Type) from lib.rs
// Input value: (AaiName, "lib.rs", Optioned value: bool is gen rust types)
hotload::gen_rust_no_mangle!((MyApi, "../call_lib/src/lib.rs", true));

// type AsyncType<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T>>>;

// #[derive(WrapperApi)]
// struct MyApi<'a> {
//     A_i32: &'a mut i32,
//     test_print: fn(msg: String),
//     init_data: fn(msg: String),
//     stop_runtime: fn(),
//     async_fn: fn() -> AsyncType<()>,
// }

/// Static variable of a dynamic library
/// call function: STATIC_VAR_DLL.test_print(xxxx);
static STATIC_VAR_DLL: Hotload<MyApi> = Hotload::new(
    #[cfg(windows)]
    "../call_lib/target/debug/call_lib.dll",
    #[cfg(not(windows))]
    "../call_lib/target/debug/libcall_lib.so",
);

fn main() {
    // init log
    std::env::set_var("RUST_LOG", "debug");
    env_logger::init();
    log::info!("env_logger starting up");

    let r = std::panic::catch_unwind(main2);
    if let Err(e) = r {
        error!("{e:?}");
    }
}

fn main2() {
    // generate rust types from lib.rs
    TestStruct { a: 1, b: 2 };

    // load successful
    let mut load_num = 0;
    STATIC_VAR_DLL
        .init_load(move |mut new_dll, mut old_dll| {
            // subscribe the dynamic library load event

            load_num += 1;

            // new dynamic library is ready, but not mounted to the static variable yet, before the new dll init same data
            new_dll.init_data(format!("load_num: {load_num}"));

            // Optional cancel mount new dll
            // new_dll.cancel();

            // drop handle, mounting mew dll, now can access the methods of the new dll
            drop(new_dll);

            // old dll is about to be unloaded, do some final work
            if let Some(old_dll) = &old_dll {
                old_dll.stop_runtime();
            }

            // sleep one second, wait for the old dll to finish its final work
            std::thread::sleep(std::time::Duration::from_millis(3500));

            // Optional cancel unload old dll
            // old_dll.cancel();

            // unload old dll
            drop(old_dll);
        })
        .unwrap();

    // new tokio runtime for async function
    let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap();
    let tokio_runtime = Arc::new(tokio_runtime);

    let mut count = 0_i128;

    tokio_runtime.spawn(async move {
        loop {
            count += 1;
            let msg = STATIC_VAR_DLL.async_run("msg");
            println!("{count}: {msg}");
        }
    });

    std::thread::sleep(std::time::Duration::from_secs(30));
}
